{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "save_and_restore_models.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "g_nWetWWd_ns"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "2pHVBk_seED1",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "N_fMsQ-N8I7j",
        "colab": {}
      },
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pZJ3uY9O17VN"
      },
      "source": [
        "# Сохранение и загрузка моделей"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "M4Ata7_wMul1"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/r1/tutorials/keras/save_and_restore_models.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запусти в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/r1/tutorials/keras/save_and_restore_models.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучай код на GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yWu9XXGieSIJ",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mBdde4YJeJKF"
      },
      "source": [
        "Прогресс обучения моделей можно сохранять во время и после обучения: тренировку можно возобновить с того места, где ты остановился. Это обычно помогает избежать долгих бесперервыных сессий обучения. Сохраняя модель, ты также можешь поделиться ею с другими, чтобы они могли воспроизвести результаты ее работы. Большинство практиков машинного обучения помимо самой модели и использованных техник также публикуют:\n",
        "\n",
        "* Код, при помощи которого обучалась модель\n",
        "* Тренировочные веса, или параметры модели\n",
        "\n",
        "Публикация этих данных помогает другим понять как работает модель, а также они смогут проверить как она ведет себя с новыми данными.\n",
        "\n",
        "Внимание! Будь осторожен с кодом, которому ты не доверяешь. Обязательно прочти [Как использовать TensorFlow безопасно?](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)\n",
        "\n",
        "### Варианты\n",
        "\n",
        "Существуют разные способы сохранять модели TensorFlow - все зависит от API, которые ты использовал в своей модели. В этом уроке используется [tf.keras](https://www.tensorflow.org/r1/guide/keras), высокоуровневый API для построения и обучения моделей в TensorFlow. Для всех остальных подходов читай руководство по TensorFlow  [Сохраняй и загружай модели](https://www.tensorflow.org/r1/guide/saved_model) или [Сохранение в Eager](https://www.tensorflow.org/r1/guide/eager#object-based_saving)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xCUREq7WXgvg"
      },
      "source": [
        "## Настройка\n",
        "\n",
        "### Настроим и импортируем зависимости"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7l0MiTOrXtNv"
      },
      "source": [
        "Установим и импортируем TensorFlow и все зависимые библиотеки:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "RzIOVSdnMYyO",
        "colab": {}
      },
      "source": [
        "!pip install h5py pyyaml"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SbGsznErXWt6"
      },
      "source": [
        "### Загрузим датасет\n",
        "\n",
        "Мы воспользуемся [датасетом MNIST](http://yann.lecun.com/exdb/mnist/) для обучения нашей модели, чтобы показать как сохранять веса. Ускорим процесс, используя только первые 1000 образцов данных:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7Nm7Tyb-gRt-",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals, unicode_literals\n",
        "\n",
        "import os\n",
        "\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "\n",
        "tf.__version__"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "9rGfFwE9XVwz",
        "colab": {}
      },
      "source": [
        "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n",
        "\n",
        "train_labels = train_labels[:1000]\n",
        "test_labels = test_labels[:1000]\n",
        "\n",
        "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n",
        "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "anG3iVoXyZGI"
      },
      "source": [
        "### Построим модель"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wynsOBfby0Pa"
      },
      "source": [
        "Давай построим простую модель, на которой мы продемонстрируем как сохранять и загружать веса моделей:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "0HZbJIjxyX1S",
        "colab": {}
      },
      "source": [
        "# Возвращает короткую последовательную модель\n",
        "def create_model():\n",
        "  model = tf.keras.models.Sequential([\n",
        "    keras.layers.Dense(512, activation=tf.nn.relu, input_shape=(784,)),\n",
        "    keras.layers.Dropout(0.2),\n",
        "    keras.layers.Dense(10, activation=tf.nn.softmax)\n",
        "  ])\n",
        "\n",
        "  model.compile(optimizer=tf.train.AdamOptimizer(),\n",
        "                loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
        "                metrics=['accuracy'])\n",
        "\n",
        "  return model\n",
        "\n",
        "\n",
        "# Создадим модель\n",
        "model = create_model()\n",
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "soDE0W_KH8rG"
      },
      "source": [
        "## Сохраняем контрольные точки"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mRyd5qQQIXZm"
      },
      "source": [
        "Основная задача заключается в том, чтобы автоматически сохранять модель как *во время*, так и *по окончании* обучения. Таким образом ты сможешь снова использовать модель без необходимости обучать ее заново, или просто продолжить с места, на котором обучение было приостановлено.\n",
        "\n",
        "Эту задачу выполняет функция обратного вызова `tf.keras.callbacks.ModelCheckpoint`. Эта функция также может быть настроена при помощи нескольких аргументов.\n",
        "\n",
        "### Использование функции\n",
        "\n",
        "Обучим нашу модель и передадим ей функцию `ModelCheckpoint`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IFPuhwntH8VH",
        "colab": {}
      },
      "source": [
        "checkpoint_path = \"training_1/cp.ckpt\"\n",
        "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
        "\n",
        "# Создадим контрольную точку при помощи callback функции\n",
        "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,\n",
        "                                                 save_weights_only=True,\n",
        "                                                 verbose=1)\n",
        "\n",
        "model = create_model()\n",
        "\n",
        "model.fit(train_images, train_labels,  epochs = 10,\n",
        "          validation_data = (test_images,test_labels),\n",
        "          callbacks = [cp_callback])  # передаем callback обучению"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rlM-sgyJO084"
      },
      "source": [
        "Это создаст одну совокупность файлов контрольных точек TensorFlow, которые обновлялись в конце каждой эпохи:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gXG5FVKFOVQ3",
        "colab": {}
      },
      "source": [
        "!ls {checkpoint_dir}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wlRN_f56Pqa9"
      },
      "source": [
        "Теперь создадим новую необученную модель. Когда мы восстанавливаем модель только из весов, новая модель должна быть точно такой же структуры, как и старая. Поскольку архитектура модели точно такая же, мы можем опубликовать веса из другой *инстанции* модели.\n",
        "\n",
        "Также мы оценим точность новой модели на проверочных данных. Необученная модель будет лишь изредка угадывать правильную категорию обзоров фильмов (точность будет около 10%):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Fp5gbuiaPqCT",
        "colab": {}
      },
      "source": [
        "model = create_model()\n",
        "\n",
        "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Необученная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1DTKpZssRSo3"
      },
      "source": [
        "А теперь загрузим веса из контрольной точки и проверим еще раз:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "2IZxbwiRRSD2",
        "colab": {}
      },
      "source": [
        "model.load_weights(checkpoint_path)\n",
        "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "bpAbKkAyVPV8"
      },
      "source": [
        "### Параметры вызова контрольной точки\n",
        "\n",
        "У callback функции есть несколько параметров, которые дают контрольным точкам уникальные имена, а также корректируют частоту сохранения.\n",
        "\n",
        "Обучим новую модель и укажем параметр чтобы сохранять контрольные точки через каждые 5 эпох:\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mQF_dlgIVOvq",
        "colab": {}
      },
      "source": [
        "# Укажем эпоху в имени файла (переведем ее в строки при помощи `str.format`)\n",
        "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n",
        "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
        "\n",
        "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n",
        "    checkpoint_path, verbose=1, save_weights_only=True,\n",
        "    # Сохраняем веса через каждые 5 эпох\n",
        "    period=5)\n",
        "\n",
        "model = create_model()\n",
        "model.fit(train_images, train_labels,\n",
        "          epochs = 50, callbacks = [cp_callback],\n",
        "          validation_data = (test_images,test_labels),\n",
        "          verbose=0)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1zFrKTjjavWI"
      },
      "source": [
        "Теперь посмотрим на получившиеся контрольные точки и выберем последнюю:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "p64q3-V4sXt0",
        "colab": {}
      },
      "source": [
        "! ls {checkpoint_dir}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "1AN_fnuyR41H",
        "colab": {}
      },
      "source": [
        "latest = tf.train.latest_checkpoint(checkpoint_dir)\n",
        "latest"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Zk2ciGbKg561"
      },
      "source": [
        "Помни: по умолчанию TensorFlow сохраняет только 5 последних контрольных точек.\n",
        "\n",
        "Для проверки восстановим модель по умолчанию и загрузим последнюю контрольную точку:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3M04jyK-H3QK",
        "colab": {}
      },
      "source": [
        "model = create_model()\n",
        "model.load_weights(latest)\n",
        "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "c2OxsJOTHxia"
      },
      "source": [
        "## Как выглядят эти файлы?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JtdYhvWnH2ib"
      },
      "source": [
        "Код выше сохраняет веса модели как совокупность [контрольных точек](https://www.tensorflow.org/r1/guide/saved_model#save_and_restore_variables) - форматированных файлов, которые содержат только обученные веса в двоичном формате. Они включают в себя:\n",
        "* Один или несколько шардов (shard, пер. \"Часть данных\"), в которых хранятся веса твоей модели\n",
        "* Индекс, который указывает какие веса хранятся в каждом шарде\n",
        "\n",
        "Если ты обучаешь модель на одном компьютере, то тогда у тебя будет всего один шард, оканчивающийся на `.data-00000-of-00001`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S_FA-ZvxuXQV"
      },
      "source": [
        "## Сохраняем веса вручную\n",
        "\n",
        "Выше мы посмотрели как загружать веса в модель.\n",
        "\n",
        "Сохранять веса вручную так же просто, просто воспользуйся методом `Model.save_weights`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "R7W5plyZ-u9X",
        "colab": {}
      },
      "source": [
        "# Сохраняем веса\n",
        "model.save_weights('./checkpoints/my_checkpoint')\n",
        "\n",
        "# Восстанавливаем веса\n",
        "model = create_model()\n",
        "model.load_weights('./checkpoints/my_checkpoint')\n",
        "\n",
        "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kOGlxPRBEvV1"
      },
      "source": [
        "## Сохраняем модель целиком\n",
        "\n",
        "Ты также можешь сохранить модель целиком в единый файл, который будет содержать все веса, конфигурацию модели и даже оптимизатор конфигурации (однако это зависит от выбранных параметров). Это позволит тебе восстановить модель и продолжить обучение позже, ровно с того момента, где ты остановился, и без правки изначального кода.\n",
        "\n",
        "Сохранять рабочую модель полностью весьма полезно. Например, ты можешь потом восстановить ее в TensorFlow.js ([HDF5](https://js.tensorflow.org/r1/tutorials/import-keras.html), [Сохраненные модели](https://js.tensorflow.org/r1/tutorials/import-saved-model.html)) и затем обучать и запускать ее в веб-браузерах, или конвертировать ее в формат для мобильных устройств, используя TensorFlow Lite ([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Сохраненные модели](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SkGwf-50zLNn"
      },
      "source": [
        "### Сохраняем в формате HDF5\n",
        "\n",
        "В Keras есть встроенный формат для сохранения модель при помощи стандарта [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format). Для наших целей сохраненная модель будет использована как единый двоичный объект *blob*."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "m2dkmJVCGUia",
        "colab": {}
      },
      "source": [
        "model = create_model()\n",
        "\n",
        "# Используй keras.optimizer чтобы восстановить оптимизатор из файла HDF5\n",
        "model.compile(optimizer=keras.optimizers.Adam(),\n",
        "              loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "model.fit(train_images, train_labels, epochs=5)\n",
        "\n",
        "# Сохраним модель полностью в единый HDF5 файл\n",
        "model.save('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GWmttMOqS68S"
      },
      "source": [
        "Теперь воссоздадим модель из этого файла:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5NDMO_7kS6Do",
        "colab": {}
      },
      "source": [
        "# Воссоздадим точно такую же модель, включая веса и оптимизатор:\n",
        "new_model = keras.models.load_model('my_model.h5')\n",
        "new_model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JXQpbTicTBwt"
      },
      "source": [
        "Проверим ее точность:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "jwEaj9DnTCVA",
        "colab": {}
      },
      "source": [
        "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dGXqd4wWJl8O"
      },
      "source": [
        "Данная техника сохраняет все:\n",
        "\n",
        "* Веса модели\n",
        "* Конфигурацию (ее структуру)\n",
        "* Параметры оптимизатора\n",
        "\n",
        "Keras сохраняет модель путем исследования ее архитектуры. В настоящее время он не может сохранять оптимизаторы TensorFlow из `tf.train`. В случае их использования нужно скомпилировать модель еще раз после загрузки. Таким образом ты получишь параметры оптимизатора.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kPyhgcoVzqUB"
      },
      "source": [
        "### Сохраняем как `saved_model`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LtcN4VIb7JkK"
      },
      "source": [
        "Обрати внимание: этот метод сохранения моделей `tf.keras` является экспериментальным и может измениться в будущих версиях."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DSWiSB0Q8c46"
      },
      "source": [
        "Построим новую модель:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sI1YvCDFzpl3",
        "colab": {}
      },
      "source": [
        "model = create_model()\n",
        "\n",
        "model.fit(train_images, train_labels, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iUvT_3qE8hV5"
      },
      "source": [
        "Создадим `saved_model`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sq8fPglI1RWA",
        "colab": {}
      },
      "source": [
        "saved_model_path = tf.contrib.saved_model.save_keras_model(model, \"./saved_models\")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "MjpmyPfh8-1n"
      },
      "source": [
        "Сохраненные модели будут помещены в папку и отмечены текущей датой и временем в названии:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ZtOvxA7V0iTv",
        "colab": {}
      },
      "source": [
        "!ls saved_models/"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "B7qfpvpY9HCe"
      },
      "source": [
        "Загрузим новую модель Keras из уже сохраненной:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "0YofwHdN0pxa",
        "colab": {}
      },
      "source": [
        "new_model = tf.contrib.saved_model.load_keras_model(saved_model_path)\n",
        "new_model"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "uWwgNaz19TH2"
      },
      "source": [
        "Запустим загруженную модель:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Pc9e6G6w1AWG",
        "colab": {}
      },
      "source": [
        "# Оптимизатор не был восстановлен, поэтому мы укажим новый\n",
        "new_model.compile(optimizer=tf.train.AdamOptimizer(),\n",
        "              loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Загруженная модель, точность: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "eUYTzSz5VxL2"
      },
      "source": [
        "## Что дальше?\n",
        "\n",
        "Это был короткий урок по сохранению и загрузке своих моделей при помощи `tf.kers`.\n",
        "\n",
        "* В [руководстве по tf.keras](https://www.tensorflow.org/r1/guide/keras) рассказывается подробнее о том, как можно сохранять и загружать модели при помощи `tf.keras`\n",
        "\n",
        "* Статья [Сохраняй в Eager](https://www.tensorflow.org/r1/guide/eager#object_based_saving) рассказывает как сохранять модель во время Eager Execution\n",
        "\n",
        "* В руководстве [Сохраняй и загружай модели](https://www.tensorflow.org/r1/guide/saved_model) содержится подробный урок обо всех низкоуровневых деталях сохранения моделей TensorFlow"
      ]
    }
  ]
}